package ferp.core.ai.strategy.simple;

import ferp.core.ai.strategy.Dropping;
import ferp.core.card.Card;
import ferp.core.game.Bid;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.log.Log;
import ferp.core.player.AI;
import ferp.core.player.Hand;

import java.util.ArrayList;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * User: igorgok
 * Date: 10/30/11 10:16 AM
 */
public class SimpleDropping extends Dropping
{
  // hash contains 12 cards
  public void initialize(int hash, boolean isStarting)
  {
    int h = hash;

    // clear the groups first
    groups.clear();

    // Create all possible drops (total of 12*11/2 = 66) dividing them into groups based on tricks estimation
    // Each group may contain more than one drop. All drops in such a group are equally priced.
    for (int i = 0; i < MAX_GROUPS; ++i)
    {
      int c1 = Hand.first(h);

      h = Hand.remove(h, c1);

      for (int c2 = Hand.first(h); c2 != 0; c2 = Hand.next(h, c2))
      {
        int pair = c1 | c2;
        Bid b = Estimate.tricks(Hand.remove(hash, pair), isStarting);
        double delta = MAX_TRICKS_10000 - b.t10000;

        ArrayList<Integer> group = groups.get(delta);

        if (group == null)
          groups.put(delta, group = new ArrayList<Integer>());

        group.add(pair);
      }
    }

    // debug output
    Set<SortedMap.Entry<Double, ArrayList<Integer>>> entries = groups.entrySet();
    int pairs = 0;

    for (SortedMap.Entry<Double, ArrayList<Integer>> g : entries)
      pairs += g.getValue().size();

    Log.debug(Log.TAG, "drop groups " + groups.size() + ", drop pairs " + pairs);

    for (SortedMap.Entry<Double, ArrayList<Integer>> g : entries)
    {
      StringBuilder sb = (new StringBuilder(512)).append("delta ").append(g.getKey()).append(": ");

      for (int pair : g.getValue())
        sb.append(Card.card(Hand.first(pair)).toString()).append(Card.card(Hand.last(pair)).toString()).append(' ');

      Log.debug(Log.TAG, sb.toString());
    }
  }

  @Override
  public void tricks(int hash, boolean isStarting, int sets, int[] drop)
  {
    initialize(hash, isStarting);

    // get the first group
    SortedMap.Entry<Double, ArrayList<Integer>> first = groups.entrySet().iterator().next();
    ArrayList<Integer> group = first.getValue();
    // choose drop "randomly" e.g. based on the number of sets played in the current game to mislead the human player
    int pair = group.get(sets % group.size());

    drop[0] = Hand.first(pair);
    drop[1] = Hand.last(pair);

    Log.debug(Log.TAG, "first drop group with delta " + first.getKey() + " of size " + group.size() + "; selected index " + sets % group.size() + ", cards " + Card.card(drop[0]) + "" + Card.card(drop[1]));
  }

  @Override
  public void misere(Game g, AI ai)
  {
    // the misere is sure, so we always drop the talon
    for (int i = 0; i < Game.TALON_SIZE; ++i)
      g.setDrop(i, g.getTalonHash(i));
  }

  @Override
  public boolean isWithout3(Game g, AI ai, Settings settings)
  {
    int[] drop = new int[Game.TALON_SIZE];
    int cards = g.getHand(ai.id()).current;

    // simulate drop for estimating the contract
    tricks(cards, ai.isStarting(g), g.sets, drop);
    cards = Hand.remove(cards, drop);

    Bid estimated = ai.strategy().contracting().tricks(cards, ai.isStarting(g));

    Log.debug(Log.TAG, "estimated bid for calculating without3: " + estimated);

    return Simulate.without3(g, settings, ai, g.bid, estimated) > Simulate.play(g, settings, ai, g.bid, estimated);
  }

  private static final int MAX_GROUPS = Game.MAXIMUM_ROUNDS + 1;
  private static final int MAX_TRICKS_10000 = Game.MAXIMUM_TRICKS * 10000;

  private final SortedMap<Double, ArrayList<Integer>> groups = new TreeMap<Double, ArrayList<Integer>>();
}
